import type { IConversationPacket } from './interface '
import type { ChatItem } from '@/types/app'

// export const isEmbed = new URLSearchParams(location.search).get("embed");

export type PostPacket<T> = {
  type: string
  action: string
  payload: T
}
export const createResponseDataReg = () => /<div data-cxr>(\s*{[\S\s]+?)<\/div>/
const parseFullMessageText = (content: string): IConversationPacket | null => {
  const result = content.match(createResponseDataReg())
  // const result = content.match(/`{3}json([\S\s]+?)`{3}/)
  if (!result) {
    console.info('[info] 解析指令失败，', content)
    return null
  }
  try {
    return JSON.parse(result[1].trim())
  }
  catch (e) {
    console.log('Bridge: error message response', e)
    return null
  }
}
type OpenChatEvent = {
  break: () => void
  ack: () => void
}
type OpenChatEventMap = {
  send2ai: (payload: { type: 'text';data: string }, ev: OpenChatEvent) => void
  answer2user: (payload: { type: 'text';data: string }, ev: OpenChatEvent) => void
}
type CommonOpenChatAckObject = {
  code: 0
  message: string
  error: number | null
}
let chatBridge: null | {
  resolve: (item: ChatItem) => void
  handshake: () => void
  on: <K extends keyof OpenChatEventMap>(action: K, callback: OpenChatEventMap[K]) => (() => void)
} = null

export const creatChatBridge = () => {
  const embed = true // remote !== window
  let prepared = false
  let shakeOk = false
  let remote: MessagePort | null = null
  const createMessage = (type: string, action: string, payload: any) => {
    return {
      type,
      action,
      payload,
    }
  }
  const post = (type: string, action: string, payload: any, remote: MessageEventSource = window.parent) => {
    const msg = createMessage(type, action, payload)
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-expect-error
    const channel = window.__qChannel
    if (channel) {
      channel.postMessage(msg)
      return
    }
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-expect-error
    remote.postMessage(msg, '*')
  }
  const post2qt = (type: string, action: string, payload: any) => {
    const msg = createMessage(type, action, payload)
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-expect-error
    const channel = window.__qChannel
    if (channel)
      channel.postMessage(msg)
  }
  const post2port = (type: string, action: string, payload: any, remote: MessagePort) => {
    const msg = createMessage(type, action, payload)
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-expect-error
    const channel = window.__qChannel
    if (channel) {
      channel.postMessage(msg)
      return
    }
    remote.postMessage(msg)
  }
  const listeners = new Map<string, Set<Function>>()
  const dispatch = (e: MessageEvent, remote: MessageEventSource = window.parent) => {
    const data = e.data as PostPacket<{
      message: string
    }>
    const {
      action,
      payload,
    } = data
    if (!listeners.has(action)) {
      console.warn(`XZC: no handler for action#${action}`)
      return
    }
    const callbacks = listeners.get(action)!
    for (const cb of callbacks) {
      let shouldBreak = false
      let acked = false
      const ev = {
        ack: (params?: Partial<CommonOpenChatAckObject> | null) => {
          if (acked)
            return

          const payload = !params
            ? {
              code: 0,
              message: 'ok',
              error: null,
            }
            : {
              code: -1,
              message: 'error',
              error: null,
              ...params,
            }
          post('xz-chat', `${action}_return`, payload, remote!)
          acked = true
        },
        break: () => {
          shouldBreak = true
        },
      }
      cb(payload, ev)
      if (shouldBreak)
        break
    }
  }
  window.addEventListener('message', (e) => {
    if (!e.source || e.data.type !== 'xz-chat')
      return
    console.log('XZM:', e)
    const data = e.data as PostPacket<{
      message: string
    }>
    const {
      action,
    } = data
    if (!shakeOk && action === 'hello') {
      if (!e.ports[0]) {
        console.error('handshake failed')
        return
      }
      shakeOk = true
      remote = e.ports[0]
      e.ports[0].onmessage = (e) => {
        if (e.data.type !== 'xz-chat')
          return
        if (!remote)
          return
        dispatch(e, remote)
      }
      return
    }
    dispatch(e)
  })
  return {
    resolve: (item: ChatItem) => {
      if (!embed)
        return

      const result = parseFullMessageText((item.content as string) || item.agent_thoughts?.[0]?.thought || '')
      if (!result)
        return
      if (!remote) {
        post2qt('xz-chat', 'directive-load', { data: result })
        return
      }

      post2port('xz-chat', 'directive-load', { data: result }, remote)
    },
    handshake: () => {
      if (!embed || prepared)
        return

      post('xz-chat', 'handshake', { })
      prepared = true
    },
    on: (action: string, callback: Function) => {
      if (!listeners.has(action))
        listeners.set(action, new Set())

      const handler = (...args: any[]) => callback(...args)
      listeners.get(action)!.add(handler)

      return () => {
        listeners.get(action)!.delete(handler)
      }
    },
  }
}

export const getChatBridge = () => {
  if (!chatBridge)
    chatBridge = creatChatBridge()

  return chatBridge
}
